package org.example.service.impl;

import jakarta.annotation.PostConstruct;
import org.example.dataobject.TrieNode;
import org.example.service.ItemService;
import org.example.service.TrieService;
import org.example.service.model.ItemModel;
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author xietl
 * @date 2025/10/28  14:55
 * @description 前缀树的实现类
 */
@Service
public class TrieServiceImpl implements TrieService {
    private static final Logger logger = LoggerFactory.getLogger(TrieServiceImpl.class);

    // Trie树根节点
    private TrieNode root;

    // 读写锁，保证并发安全
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    @Autowired
    private ItemService itemService;

    @Autowired
    private RedisTemplate jsonRedisTemplate;

    // Redis中Trie树的键名
    private static final String TRIE_TREE_REDIS_KEY = "trie_tree";

    //初始化前缀树
    @PostConstruct
    public void init(){
        logger.info("初始化Trie树服务");
        root = new TrieNode();
        buildTrieTree();
    }


    @Override
    public void insertItemKeyWord(ItemModel item) {
        if (item == null || item.getTitle() == null || item.getTitle().isEmpty()) {
            return;
        }
        lock.writeLock().lock();//获取写锁
        try{
            String title = item.getTitle().toLowerCase(); // 转换为小写以支持大小写不敏感搜索
            TrieNode current = root;
            for(char ch:title.toCharArray()){
                //如果子节点不存在，则创建新节点。
                current.getChildren().computeIfAbsent(ch,c->new TrieNode());
                //移动到子节点
                current = current.getChildren().get(ch);
            }
            current.setEndOfWord(true);//标记为单词结束
            current.addItem(item.getId());//添加商品Id

            logger.debug("插入商品索引 {} 到前缀树节点 {}", item.getId(), title);

        }finally {
            lock.writeLock().unlock();//释放写锁
        }
    }

    @Override
    public List<ItemModel> searchItemsByPrefix(String prefix, int limit) {
       if(prefix == null || prefix.isEmpty()){
           return null;
       }
       lock.readLock().lock();
       try{
           String lowerPrefix = prefix.toLowerCase(); // 转换为小写以支持大小写不敏感搜索
           TrieNode current = root;
           for(char ch:lowerPrefix.toCharArray()){
               TrieNode node = current.getChildren().get(ch);
               if(node == null){
                   return new ArrayList<>();//前缀不存在
               }
               current = node;
           }
           //获取所有以该前缀结尾的商品索引
           List<ItemModel> itemIds = new ArrayList<>();
           collectItems(current,itemIds,limit);


           // 如果结果不足限制数量，尝试从Redis缓存获取更多结果
           if (itemIds.size() < limit) {
               // 从Redis获取热门搜索结果作为补充
               List<ItemModel> cachedResults = getCachedSearchResults(lowerPrefix, limit - itemIds.size());
               itemIds.addAll(cachedResults);
           }

           //根据索引查询商品详情
           return itemIds.subList(0, Math.min(itemIds.size(), limit));

       } finally {
           lock.readLock().unlock();
       }

    }

    /**
     * 递归收集节点下所有商品
     * @param node
     * @param result
     * @param limit
     */
    private void collectItems(TrieNode node, List<ItemModel> result, int limit) {
        if (result.size() >= limit) {
            return;
        }

        // 如果是单词结尾，获取对应的商品
        if (node.isEndOfWord()) {
            for (Integer itemId : node.getItemIds()) {
                if (result.size() >= limit) {
                    return;
                }
                try {
                    ItemModel item = itemService.getItemById(itemId);
                    if (item != null) {
                        result.add(item);
                    }
                } catch (Exception e) {
                    logger.error("获取商品信息失败，ID: {}", itemId, e);
                }
            }
        }

        // 递归遍历所有子节点
        for (TrieNode child : node.getChildren().values()) {
            if (result.size() >= limit) {
                return;
            }
            collectItems(child, result, limit);
        }
    }

    /**
     * 把数据库中的数据构建到前缀树中，用于搜索商品关键词。
     */
    @Override
    public void buildTrieTree() {
        logger.info("开始构建Trie树");
        lock.writeLock().lock();
        try {
            // 清空现有树
            root = new TrieNode();

            // 从数据库加载所有商品
            List<ItemModel> items = itemService.listItem();
            logger.info("从数据库加载了 {} 个商品", items.size());

            // 将所有商品插入Trie树
            for (ItemModel item : items) {
                insertItemKeyWord(item);
            }

            logger.info("Trie树构建完成，共包含 {} 个商品", items.size());
        } catch (Exception e) {
            logger.error("构建Trie树时发生错误", e);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 从Redis缓存获取搜索结果
     * @param prefix 前缀
     * @param limit 限制数量
     * @return 商品列表
     */
    @SuppressWarnings("unchecked")
    private List<ItemModel> getCachedSearchResults(String prefix, int limit) {
        try {
            String key = "search_suggestions:" + prefix;
            List<Object> cachedIds = (List<Object>) jsonRedisTemplate.opsForList().range(key, 0, limit - 1);

            if (cachedIds != null && !cachedIds.isEmpty()) {
                List<ItemModel> result = new ArrayList<>();
                for (Object idObj : cachedIds) {
                    if (result.size() >= limit) {
                        break;
                    }
                    try {
                        Integer itemId = Integer.valueOf(idObj.toString());
                        ItemModel item = itemService.getItemById(itemId);
                        if (item != null) {
                            result.add(item);
                        }
                    } catch (Exception e) {
                        logger.error("从缓存获取商品信息失败，ID: {}", idObj, e);
                    }
                }
                return result;
            }
        } catch (Exception e) {
            logger.error("从Redis获取缓存搜索结果时发生错误", e);
        }
        return new ArrayList<>();
    }
}
